home *** CD-ROM | disk | FTP | other *** search
/ NeXT Education Software Sampler 1992 Fall / NeXT Education Software Sampler 1992 Fall.iso / Programming / Classes / SocketClasses / SktSocketUser.m < prev    next >
Encoding:
Text File  |  1992-07-23  |  41.2 KB  |  945 lines

  1. /***************************************************************************
  2. *                                                                          *
  3. * SktSocketUser.m                                                          *
  4. * Copyright 1992 by Nik A Gervae                                           *
  5. *                                                                          *
  6. * One of a set of three Objective-C classes (SktSocketManager, SktSocket,  *
  7. * and SktSocketUser) which implement a convenient interface to Berkeley    *
  8. * stream sockets under NeXTSTEP(r).  See the accompanying class            *
  9. * specifications (files with a .rtf or .spec suffix) for further           *
  10. * information.                                                             *
  11. *                                                                          *
  12. * NeXTSTEP is a registered trademark of NeXT Computer, Inc.                *
  13. *                                                                          *
  14. ****************************************************************************
  15. *                                                                          *
  16. * LICENSE                                                                  *
  17. *                                                                          *
  18. * This program is free software; you can redistribute it and/or modify     *
  19. * it under the terms of the GNU General Public License as published by     *
  20. * the Free Software Foundation.                                            *
  21. *                                                                          *
  22. * The program and this makefile are distributed in the hope that it will   *
  23. * be useful, but are provided "AS IS" AND WITHOUT ANY WARRANTY; without    *
  24. * any express or implied warranty of MERCHANTABILITY or FITNESS FOR A      *
  25. * PARTICULAR PURPOSE. See the GNU General Public License for more details. *
  26. * Any use or distribution of the program and documentation must include    *
  27. * appropriate copyrights to acknowledge Nik A. Gervae and the Free         *
  28. * Software Foundation, Inc.                                                *
  29. *                                                                          *
  30. * You should have received a copy of the GNU General Public License        *
  31. * along with this program; if not, write to the Free Software              *
  32. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.                *
  33. *                                                                          *
  34. ****************************************************************************
  35. *                                                                          *
  36. * VERSION HISTORY                                                          *
  37. *                                                                          *
  38. * Version numbers are simply dates in the form YYYYMMDD.  These represent  *
  39. * the date that version was finished.  Only significantly changed versions *
  40. * are reported here, or those versions requiring explanation of changes.   *
  41. * There may be many interim stages between dated versions.                 *
  42. *                                                                          *
  43. * DateVersion Primary Author  Notes                                        *
  44. * ----------- --------------- -------------------------------------------- *
  45. * 19920327    Nik A Gervae    First released version                       *
  46. * 19920723    Nik A Gervae    Actually released                            *
  47. *                                                                          *
  48. ***************************************************************************/
  49.  
  50. #import <stdio.h>
  51. #import <string.h>
  52.  
  53. #import "SktSocketUser.h"
  54.  
  55.  
  56. typedef struct { 
  57.   @defs(SktSocket)
  58. } sktsocket;
  59.  
  60. /*
  61.  * Most users will want lines of text.
  62.  */
  63. #define DFLT_DELIMITER '\n'
  64.  
  65. /*
  66.  * See +initialize below for info on these.
  67.  */
  68. typedef char *(*inpToCharFunc)(id self, SEL _cmd, char aChar,
  69.                                  NXZone *aZone, ...);
  70. typedef id (*qOutputFunc)(id self, SEL _cmd, const char *output,
  71.                                  long int length, ...);
  72. static inpToCharFunc inputToCharInZone;
  73. static qOutputFunc   outputToSocket;
  74.  
  75. /***************************************************************************
  76. *                                                                          *
  77. * SktReportUserMemError()                                                  *
  78. *                                                                          *
  79. * Report appropriately based on whether the process is a server (has an    *
  80. * SktSocketManager), or a client.                                          *
  81. *                                                                          *
  82. ***************************************************************************/
  83. void SktReportUserMemError(id self, char *message) {
  84.  
  85.   id socket;
  86.  
  87.   socket = [self socket];
  88.  
  89.   if ([socket manager]) {
  90.     [[socket manager] log:message, [self name], [socket socketFd]];
  91.   }
  92.   else if (stderr) {
  93.     fprintf(stderr, message, [self name], [socket socketFd]);
  94.   }
  95.   return;
  96. }
  97.  
  98. /***************************************************************************
  99. *                                                                          *
  100. * These are the constant strings used.  Feel free to translate them into   *
  101. * your favorite language.  Do be sure to keep all the % directives in      *
  102. * place, or change the code that accesses these strings.                   *
  103. *                                                                          *
  104. ***************************************************************************/
  105. #define STR_Error              "ERROR (%s %d):"
  106. #define STR_ReallocFatalError  STR_Error "zone realloc failed.\n"
  107. #define STR_MallocFatalError   STR_Error "zone malloc failed.\n"
  108.  
  109.  
  110. @implementation SktSocketUser
  111.  
  112.  
  113. /***************************************************************************
  114. *                                                                          *
  115. * +initialize                                                              *
  116. *                                                                          *
  117. * This method caches the function pointers for the inputToChar:inZone: and *
  118. * SktSocket's queueOutput:ofLength: methods.  Since these methods are      *
  119. * covered by other methods, we want to avoid a message send each time a    *
  120. * cover is used.  This way, we only incur the cost of a function call.     *
  121. * Hopefully that's faster. :-)                                             *
  122. *                                                                          *
  123. ***************************************************************************/
  124. + initialize
  125. {
  126.   static BOOL initted = NO;
  127.  
  128.  /*
  129.   * Only do this if we're initializing the class itself.  Having a
  130.   * subclass do it only resets the static variables, which is a waste.
  131.   */
  132.   if (NO == initted) {
  133.     inputToCharInZone = (inpToCharFunc)
  134.       [self instanceMethodFor:@selector(inputToChar:inZone:)];
  135.     outputToSocket = (qOutputFunc)
  136.       [[SktSocket class] instanceMethodFor:@selector(queueOutput:ofLength:)];
  137.     initted = YES;
  138.   }
  139.   return self;
  140. }
  141.  
  142. /***************************************************************************
  143. *                                                                          *
  144. * -initWithSocket:                                                         *
  145. *                                                                          *
  146. * THIS IS THE DESIGNATED INITIALIZER FOR THIS CLASS.                       *
  147. *                                                                          *
  148. * Initializes the user with a particular SktSocket object for its I/O.     *
  149. * Various ivars are set up here.  By default, an SktSocketUser strips the  *
  150. * delimiter from its input, and CRLFs after that.  An initial empty queue  *
  151. * is allocated, and no queue limit is imposed (0 means no limit).  The     *
  152. * delimiter is by default LF, since most often a user will probably be     *
  153. * processing lines of text.  Logging of non-error messages is also enabled *
  154. * by default.                                                              *
  155. *                                                                          *
  156. * If you want to be clever, this class, together with the SktSocket class, *
  157. * fully supports using NULL-terminated strings, so you could use NULL as   *
  158. * the delimiter, provided NULLs can get sent across the connection.        *
  159. *                                                                          *
  160. * ERROR CONDITION: If the allocation fails, a message is logged, and nil   *
  161. * is returned.  The object will not exist if this method returns nil, so   *
  162. * you should consider a nil return as a fatal condition and exit the       *
  163. * program.                                                                 *
  164. *                                                                          *
  165. ***************************************************************************/
  166. - initWithSocket:(SktSocket *)aSocket
  167. {
  168.   [super init];
  169.  
  170.   zone = [self zone];
  171.   socket = nil;
  172.   [self setSocket:aSocket];  // Helps in subclassing....
  173.  
  174.   doesStrip     = YES;
  175.   doesStripCRLF = YES;
  176.  
  177.   inputQueue    = (char *)NXZoneMalloc(zone, INQSIZE);
  178.   if (!inputQueue) {
  179.     SktReportUserMemError(self, STR_MallocFatalError);
  180.     return [self free];
  181.   }
  182.  
  183.   queueLength   = 0;
  184.   queueLimit    = 0;  // This means "no limit"
  185.  
  186.   delimiter     = DFLT_DELIMITER;
  187.  
  188. #ifdef DEBUG
  189.   queueLimit = 3;  /// Remember to remove this sometime!
  190. #endif
  191.  
  192.   return self;
  193. }
  194.  
  195. /***************************************************************************
  196. *                                                                          *
  197. * -init                                                                    *
  198. *                                                                          *
  199. * I don't see any reason for this method, but somebody may want to create  *
  200. * a user, and then later set the socket.  This is NOT the designated       *
  201. * initializer.  It's here to catch idiot code that sends init, which would *
  202. * otherwise totally bypass the above method.                               *
  203. *                                                                          *
  204. * This method isn't in the header, and shouldn't be in the header.  I want *
  205. * people to get compiler warnings for trying to send this message, as its  *
  206. * usually the wrong thing to do.                                           *
  207. *                                                                          *
  208. ***************************************************************************/
  209. - init
  210. {
  211.   return [self initWithSocket:nil];
  212. }
  213.  
  214. /***************************************************************************
  215. *                                                                          *
  216. * -free                                                                    *
  217. *                                                                          *
  218. * The name says it all.                                                    *
  219. *                                                                          *
  220. * Note that you can lose the socket reference if you're not careful.  Be   *
  221. * sure to snag it before sending this message.                             *
  222. *                                                                          *
  223. ***************************************************************************/
  224. - free
  225. {
  226.   if (inputQueue) NXZoneFree(zone, inputQueue);
  227.  
  228.   if (socket && self == [socket user]) [socket setUser:nil];
  229.  
  230.   return [super free];
  231. }
  232.  
  233. /***************************************************************************
  234. *                                                                          *
  235. * -setDelimiter:                                                           *
  236. *                                                                          *
  237. * Sets the character used by -nextInputLine... to determine what a 'line'  *
  238. * is.  Characters up to and including this character will be yanked from   *
  239. * the queue (if possible) when nextInputLine... is used.                   *
  240. *                                                                          *
  241. ***************************************************************************/
  242. - setDelimiter:(char)aChar;
  243. {
  244.   delimiter = aChar;
  245.   return self;
  246. }
  247.  
  248. /***************************************************************************
  249. *                                                                          *
  250. * -delimiter                                                               *
  251. *                                                                          *
  252. * Returns the character used by -nextInputLine... to determine what a      *
  253. * 'line' is.  Characters up to and including this character will be yanked *
  254. * from the queue (if possible) when nextInputLine... is used.              *
  255. *                                                                          *
  256. ***************************************************************************/
  257. - (char)delimiter
  258. {
  259.   return delimiter;
  260. }
  261.  
  262. /***************************************************************************
  263. *                                                                          *
  264. * -setdoesStrip:                                                           *
  265. *                                                                          *
  266. * Allows you to set whether the delimiter is removed from the returned     *
  267. * text string when you use inputToChar... or nextInputLine....             *
  268. *                                                                          *
  269. ***************************************************************************/
  270.  
  271. - setdoesStrip:(BOOL)flag
  272. {
  273.   doesStrip = flag;
  274.   return self;
  275. }
  276.  
  277. /***************************************************************************
  278. *                                                                          *
  279. * -doesStrip                                                               *
  280. *                                                                          *
  281. * Returns YES if the delimiter is removed from the returned text string    *
  282. * when you use inputToChar... or nextInputLine..., NO if it's left alone   *
  283. * (poor thing).                                                            *
  284. *                                                                          *
  285. ***************************************************************************/
  286.  
  287. - (BOOL)doesStrip
  288. {
  289.   return doesStrip;
  290. }
  291.  
  292. /***************************************************************************
  293. *                                                                          *
  294. * -setDoesStripCRLF:                                                       *
  295. *                                                                          *
  296. * Allows you to set whether CRLFs are removed from the returned text       *
  297. * string when you use inputToChar... or nextInputLine....  If doesStrip is *                                                                          * also YES, then CRLF stripping is performed in addition to regular        *
  298. * stripping.                                                               *
  299. *                                                                          *
  300. ***************************************************************************/
  301.  
  302. - setDoesStripCRLF:(BOOL)flag
  303. {
  304.   doesStripCRLF = flag;
  305.   return self;
  306. }
  307.  
  308. /***************************************************************************
  309. *                                                                          *
  310. * -doesStripCRLF                                                           *
  311. *                                                                          *
  312. * Returns YES if CRLFs are removed from text strings returned by           *
  313. * inputToChar... or nextInputLine....  If doesStrip is also YES, then CRLF *
  314. * stripping is performed in addition to regular stripping.                 *
  315. *                                                                          *
  316. ***************************************************************************/
  317.  
  318. - (BOOL)doesStripCRLF
  319. {
  320.   return doesStripCRLF;
  321. }
  322.  
  323. /***************************************************************************
  324. *                                                                          *
  325. * -setQueueLimit:                                                          *
  326. *                                                                          *
  327. * Allows you to limit the number of 'lines' allowed in the queue at any    *
  328. * one time.  If queueLimit is positive, then when new data is added to the *
  329. * queue, the number of delimiters is counted, and if that exceeds          *
  330. * queueLimit, all of the delimited seqments of characters after are blown  *
  331. * away.                                                                    *
  332. *                                                                          *
  333. ***************************************************************************/
  334. - setQueueLimit:(long int)limit
  335. {
  336.   queueLimit = limit;
  337.  
  338.   return self;
  339. }
  340.  
  341. /***************************************************************************
  342. *                                                                          *
  343. * -queueLimit                                                              *
  344. *                                                                          *
  345. * Returns the number of 'lines' allowed in the queue at any one time.  If  *
  346. * queueLimit is positive, then when new data is added to the queue, the    *
  347. * number of delimiters is counted, and if that exceeds queueLimit, all of  *
  348. * the delimited seqments of characters after are blown away.               *
  349. *                                                                          *
  350. ***************************************************************************/
  351. - (long int)queueLimit
  352. {
  353.   return queueLimit;
  354. }
  355.  
  356. /***************************************************************************
  357. *                                                                          *
  358. * -setSocket:                                                              *
  359. *                                                                          *
  360. * Sets the SktSocket object used to that supplied.  This method also mucks *
  361. * with the old and new sockets, to properly remove self as the old         *
  362. * socket's user without causing an infinite loop.  This is doable with     *
  363. * methods, but the classes are so tightly bound, it's not really worth the *
  364. * code bulk or cost of a message send.                                     *
  365. *                                                                          *
  366. ***************************************************************************/
  367. - (SktSocket *)setSocket:(SktSocket *)aSocket
  368. {
  369.   SktSocket *oldSocket;
  370.  
  371.   oldSocket = socket;
  372.   socket = aSocket;
  373.  
  374.  /*
  375.   * We need to muck with pointers because messaging would cause an
  376.   * infinite loop.
  377.   */
  378.   if (oldSocket) ((sktsocket *)oldSocket)->user = nil;
  379.   if (socket) ((sktsocket *)socket)->user = self;
  380.  
  381.   return oldSocket;
  382. }
  383.  
  384. /***************************************************************************
  385. *                                                                          *
  386. * -socket                                                                  *
  387. *                                                                          *
  388. * Returns the SktSocket object used for I/O.                               *
  389. *                                                                          *
  390. ***************************************************************************/
  391. - (SktSocket *)socket
  392. {
  393.   return socket;
  394. }
  395.  
  396. /***************************************************************************
  397. *                                                                          *
  398. * -queueInput:ofLength:                                                    *
  399. *                                                                          *
  400. * Adds the provided segment of characters to the end of the input queue.   *
  401. * Beyond that, enforces queue limits.  If a queue limit has been set, this *
  402. * method counts the number of delimiters in the new queue, and blows away  *
  403. * all fully delimited sequences of characters past the first queueLimit    *
  404. * ones.                                                                    *  
  405. *                                                                          *
  406. * ERROR CONDITION: If the reallocation fails, a message is logged, and nil *
  407. * is returned.  The input queue will not exist if this method returns nil, *
  408. * so you should consider a nil return as a fatal condition and either free *
  409. * the object, or exit the program.                                         *
  410. *                                                                          *
  411. ***************************************************************************/
  412. - queueInput:(const char *)input ofLength:(long int)length
  413. {
  414.   long int fullLength;    // the full length of the queue after appending
  415.   char     delimiterCache;  // the line delimiter (cached for this call)
  416.   long int queueLimitCache;
  417.  
  418.  /*
  419.   * I hope nobody is this stupid.
  420.   */
  421.   if (0 >= length) return self;
  422.  
  423.  /*
  424.   * If there's no input queue, we've got trouble!
  425.   */
  426.   if (!inputQueue) return nil;
  427.  
  428.  /*
  429.   * Cache the delimiter, since we'll be using it a few times.
  430.   */
  431.   delimiterCache = [self delimiter];
  432.   queueLimitCache = [self queueLimit];
  433.  
  434.  /*
  435.   * Find out how much text we're dealing with, check if we need more space,
  436.   * and smash 'em together.  Update the length.
  437.   */
  438.   fullLength = queueLength + length;
  439.  
  440.  /*
  441.   * If the queue needs to be bigger, reallocate it.  Watch for
  442.   * a fatal error.
  443.   */
  444.   if (malloc_size(inputQueue) <= fullLength) {
  445.  
  446.     inputQueue = (char *)NXZoneRealloc(zone, inputQueue, fullLength);
  447.  
  448.     if (!inputQueue) {
  449.       SktReportUserMemError(self, STR_ReallocFatalError);
  450.       queueLength = 0;
  451.       return nil;
  452.     }
  453.   }
  454.  
  455.   memcpy(inputQueue+queueLength, input, length);
  456.   queueLength = fullLength;
  457.  
  458.  /*
  459.   * Here's the hairy part.  If queue limits have been imposed, we
  460.   * do a bunch of stuff:
  461.   */
  462.   if (0 < queueLimitCache) {
  463.  
  464.     long int lastLineEnd;   // char of queue beyond last line
  465.     long int lastFrag;      // beginning of non-delimited text
  466.     long int segtsInQueue;  // # lines waiting to be processed
  467.  
  468.  
  469.    /*
  470.     * 1. Count up how many lines we can add to the queue and record the
  471.     *    position of the end.
  472.     */
  473.     for (segtsInQueue = 0, lastLineEnd = 0; lastLineEnd < queueLength;
  474.          lastLineEnd++) {
  475.  
  476.       if (inputQueue[lastLineEnd] == delimiterCache) segtsInQueue++;
  477.       if (segtsInQueue >= queueLimitCache) break;
  478.     }
  479.     lastLineEnd++;  // This may be past the buffer; that's OK.
  480.  
  481.    /*
  482.     * 2. Now, if we didn't cover the entire queue, the limit was
  483.     *    exceeded.  We have to pull some text back, so that any
  484.     *    partially complete segments (fragments) can be completed
  485.     *    later.
  486.     */
  487.     if (lastLineEnd < queueLength) {
  488.  
  489.      /*
  490.       * 3. Get the start of the last fragment line
  491.       *   (line without delimiter).
  492.       */
  493.       for (lastFrag = queueLength; 0 <= lastFrag; lastFrag--) {
  494.         if (delimiterCache == inputQueue[lastFrag]) {
  495.           lastFrag++;  // This may be past the buffer; that's OK (we check).
  496.           break;
  497.         }
  498.       }
  499.  
  500.      /*
  501.       * 4. If there are too many lines in the queue, wipe them out,
  502.       *    pulling the fragment line over them so it isn't lost.
  503.       *    If there is no fragment (lastFrag == queueLength), don't
  504.       *    pull anything.  Last, update the queue length.
  505.       */
  506.       if (lastLineEnd < queueLength && lastFrag > lastLineEnd) {
  507.         if (lastFrag < queueLength) {
  508.           memmove(inputQueue+lastLineEnd, inputQueue+lastFrag,
  509.                   queueLength-lastFrag);
  510.         }
  511.         queueLength = lastLineEnd + queueLength - lastFrag;
  512.       }
  513.  
  514.     } /*if (lastLineEnd < queueLength)*/
  515.  
  516.   } /*if (0 < queueLimitCache)*/
  517.  
  518.   return self;
  519.  
  520. } /*queueInput:ofLength:*/
  521.  
  522. /***************************************************************************
  523. *                                                                          *
  524. * -purgeInput                                                              *
  525. *                                                                          *
  526. * Marks the queue as empty, and shrinks it if needed.                      *
  527. *                                                                          *
  528. * ERROR CONDITION: If the reallocation fails, a message is logged, and nil *
  529. * is returned.  The input queue will not exist if this method returns nil, *
  530. * so you should consider a nil return as a fatal condition and either free *
  531. * the object, or exit the program.                                         *
  532. *                                                                          *
  533. ***************************************************************************/
  534. - purgeInput
  535. {
  536.   queueLength = 0;
  537.  
  538.  /*
  539.   * Reallocate the queue to be the base size.  Since the realloc
  540.   * is only shrinking the buffer, this really has no reason ever
  541.   * to fail, but check anyway.
  542.   */
  543.   if (malloc_size(inputQueue) > INQSIZE) {
  544.  
  545.     inputQueue = (char *)NXZoneRealloc(zone, inputQueue, INQSIZE);
  546.  
  547.     if (!inputQueue) {
  548.       SktReportUserMemError(self, STR_ReallocFatalError);
  549.       queueLength = 0;
  550.       return nil;
  551.     }
  552.  
  553.   }
  554.  
  555.   return self;
  556. }
  557.  
  558. /***************************************************************************
  559. *                                                                          *
  560. * -queueOutput:ofLength:                                                   *
  561. *                                                                          *
  562. * A cover for SktSockets method for queuing output.  This should always be *
  563. * used by the user instead of directly accessing the socket, as this       *
  564. * method may be changed to do special processing on request.               *
  565. *                                                                          *
  566. ***************************************************************************/
  567. - queueOutput:(const char *)output ofLength:(long int)length
  568. {
  569.   (*outputToSocket)([self socket], @selector(queueOutput:ofLength:),
  570.                           output, length);
  571.   return self;
  572. }
  573. /***************************************************************************
  574. *                                                                          *
  575. * -queueOutputString:                                                      *
  576. *                                                                          *
  577. * A cover for SktSockets method for queuing output.  This should always be *
  578. * used by the user instead of directly accessing the socket, as this       *
  579. * method may be changed to do special processing on request.               *
  580. *                                                                          *
  581. ***************************************************************************/
  582. - queueOutputString:(const char *)aString
  583. {
  584.  /*
  585.   * Note that this is sent to the *socket*, not to self.
  586.   */
  587.   (*outputToSocket)([self socket], @selector(queueOutput:ofLength:),
  588.                           aString, strlen(aString));
  589.   return self;
  590. }
  591.  
  592. /***************************************************************************
  593. *                                                                          *
  594. * -queueLength                                                             *
  595. *                                                                          *
  596. * Returns the size in bytes of the input queue.                            *
  597. *                                                                          *
  598. ***************************************************************************/
  599. - (long int)queueLength
  600. {
  601.   return queueLength;
  602. }
  603.  
  604. /***************************************************************************
  605. *                                                                          *
  606. * getInput:ofLength:orLess:                                                *
  607. *                                                                          *
  608. * Retrieves length characters from the input queue, putting them into the  *
  609. * buffer provided, and returning the actual number of characters retrieved *
  610. * (it may be less if the queue is shorter than the requested length).  If  *
  611. * the orLess: flag is NO, then no input will be retrieved if the full      *
  612. * length specified can't be retrieved, and zero will be returned.          *
  613. *                                                                          *
  614. * THIS METHOD DOES NOT, CANNOT, AND SHOULD NOT DO ANY STRIPPING WHATEVER.  *
  615. *                                                                          *
  616. * ERROR CONDITION: If the reallocation fails, a message is logged, and -1  *
  617. * is returned.  The input queue will not exist if this method returns -1,  *
  618. * so you should consider a -1 return as a fatal condition and either free  *
  619. * the object, or exit the program.                                         *
  620. *                                                                          *
  621. ***************************************************************************/
  622. - (long int)getInput:(char *)input ofLength:(long int)length orLess:(BOOL)flag
  623. {
  624.   long int effectiveLength;
  625.  
  626.  /*
  627.   * I hope nobody is this stupid.
  628.   */
  629.   if (0 >= length) return 0;
  630.  
  631.  /*
  632.   * If there's no input queue, we've got trouble!
  633.   */
  634.   if (!inputQueue) return -1;
  635.  
  636.   effectiveLength = length < queueLength ? length : queueLength;
  637.   if (NO ==flag && effectiveLength < length) return 0;
  638.   memcpy(input, inputQueue, effectiveLength);
  639.  
  640.   if (0 < queueLength-effectiveLength) {
  641.     memmove(inputQueue, inputQueue+effectiveLength,
  642.             queueLength-effectiveLength);
  643.   }
  644.   queueLength -= effectiveLength;
  645.  
  646.  /*
  647.   * If the queue has shrunk to a length signigicantly smaller than
  648.   * its buffer size, reallocate it to be smaller.  This really has
  649.   * no reason ever to fail, but check anyway.
  650.   */
  651.   if (queueLength < INQSIZE/2 && malloc_size(inputQueue) > INQSIZE) {
  652.  
  653.     inputQueue = (char *)NXZoneRealloc(zone, inputQueue, INQSIZE);
  654.  
  655.     if (!inputQueue) {
  656.       SktReportUserMemError(self, STR_ReallocFatalError);
  657.       queueLength = 0;
  658.       return -1;
  659.     }
  660.  
  661.   }
  662.  
  663.   return effectiveLength;
  664. }
  665.  
  666. /***************************************************************************
  667. *                                                                          *
  668. * -ungetInput:ofLength:                                                    *
  669. *                                                                          *
  670. * Use this method to put back a sequence of characters retrieved from the  *
  671. * queue.  The sequence of characters should be exactly the same as it was  *
  672. * IN THE QUEUE (not as it was returned), or all bets are off.  In          *
  673. * In particular, you should only use this method to put back characters    *
  674. * retrieved with getInput:ofLength:orLess:, as it is the only retrieval    *
  675. * method that is guaranteed not to muck with the queue or the retrieved    *
  676. * characters.                                                              *
  677. *                                                                          *
  678. * Especially do NOT use this method to undo the effects of inputToChar...  *
  679. * or nextInputLine....  Those methods may strip from input the very        *
  680. * characters needed to retrieve a proper segment.                          *
  681. *                                                                          *
  682. * ERROR CONDITION: If the reallocation fails, a message is logged, and nil *
  683. * is returned.  The input queue will not have been restored if this method *
  684. * returns nil,  so you should consider a nil return as a fatal condition   *
  685. * and either free the object, or exit the program.                         *
  686. *                                                                          *
  687. ***************************************************************************/
  688. - ungetInput:(char *)input ofLength:(long int)length
  689. {
  690.   long int  fullLength;   // 
  691.   long int  lastLineEnd;  // char of queue beyond last line; for queue limits
  692.   long int  lastFrag;     // beginning of non-newline terminated text
  693.   char   *newQueue;
  694.  
  695.  /*
  696.   * I hope nobody is this stupid.
  697.   */
  698.   if (0 >= length) return self;
  699.  
  700.  /*
  701.   * If there's no input queue, we've got trouble!
  702.   */
  703.   if (!inputQueue) return nil;
  704.  
  705.  /*
  706.   * Find out how much text we're dealing with, check if we need more space,
  707.   * and smash 'em together.  Update the length.
  708.   */
  709.   fullLength = queueLength + length;
  710.  
  711.  /*
  712.   * Now muck with everything and make it like it should be.
  713.   * Watch for a fatal error.
  714.   */
  715.   newQueue = (char *)NXZoneMalloc(zone, fullLength);
  716.  
  717.   if (!newQueue) {
  718.     SktReportUserMemError(self, STR_MallocFatalError);
  719.     queueLength = 0;
  720.     return nil;
  721.   }
  722.  
  723.   memcpy(newQueue, input, length);
  724.   memcpy(newQueue+length, inputQueue, queueLength);
  725.  
  726.   NXZoneFree(zone, inputQueue);
  727.   inputQueue = newQueue;
  728.   queueLength = fullLength;
  729.  
  730.   return self;
  731. }
  732.  
  733. /***************************************************************************
  734. *                                                                          *
  735. * -getAllInput:                                                            *
  736. *                                                                          *
  737. * Returns by reference the entire input queue, and returns its length.     *
  738. *                                                                          *
  739. * ERROR CONDITION: If the allocation fails, a message is logged, and -1 is *
  740. * returned.  The input queue will not have been restored if this method    *
  741. * returns -1,  so you should consider a  return of -1 as a fatal condition *
  742. * and either free the object, or exit the program.                         *
  743. *                                                                          *
  744. ***************************************************************************/
  745. - (long int)getAllInput:(char **)queue
  746. {
  747.   char *newQueue;
  748.   long int oldQueueLength;
  749.  
  750.   if (!queueLength) return 0;
  751.  
  752.  /*
  753.   * If there's no input queue, we've got trouble!
  754.   */
  755.   if (!inputQueue) return -1;
  756.  
  757.   *queue = inputQueue;
  758.   oldQueueLength = queueLength;
  759.   queueLength = 0;
  760.  
  761.   inputQueue = (char *)NXZoneMalloc(zone, INQSIZE);
  762.   if (!inputQueue) {
  763.     SktReportUserMemError(self, STR_MallocFatalError);
  764.     queueLength = 0;
  765.     return -1;
  766.   }
  767.  
  768.   return oldQueueLength;
  769. }
  770.  
  771. /***************************************************************************
  772. *                                                                          *
  773. * -inputToChar:                                                            *
  774. *                                                                          *
  775. * This method is a cover for -inputToChar:inZone:.  It invokes that method *
  776. * with NXDefaultMallocZone() as the zone argument.                         *
  777. *                                                                          *
  778. * ERROR CONDITION: If the allocation fails, a message is logged, and NULL  *
  779. * is returned.  Since this is indistinguishable from a situation in which  *
  780. * there is nothing in the queue, you can't currently catch a malloc        *
  781. * failure in this method.                                                  *
  782. *                                                                          *
  783. ***************************************************************************/
  784. - (char *)inputToChar:(char)aChar;
  785. {
  786.   return (char *)(*inputToCharInZone)(self, @selector(inputToChar:inZone:),
  787.                              aChar, NXDefaultMallocZone());
  788. }
  789.  
  790. /***************************************************************************
  791. *                                                                          *
  792. * -inputToChar:inZone:                                                     *
  793. *                                                                          *
  794. * Scans the input queue for the character provided, and pulls characters   *
  795. * from the queue up to and including that character.  The characters are   *
  796. * copied to a buffer allocated from the specified zone.  Further, if       *
  797. * stripping is enabled, the delimiter is removed from the retrieved        *
  798. * string.  If CRLF stripping is enabled, all trailing CRLFs are removed    *
  799. * (after the non-CRLF delimiter is removed if regular stripping is also    *
  800. * enabled).  Note that the return value is a NULL-terminated character     *
  801. * string (which may be NULL or empty.                                      *
  802. *                                                                          *
  803. * ERROR CONDITION: If the allocation fails, a message is logged, and NULL  *
  804. * is returned.  Since this is indistinguishable from a situation in which  *
  805. * there is nothing in the queue, you can't currently catch a malloc        *
  806. * failure in this method.                                                  *
  807. *                                                                          *
  808. ***************************************************************************/
  809. - (char *)inputToChar:(char)aChar inZone:(NXZone *)aZone;
  810. {
  811.   char     *nextInput;
  812.   long int  rest;
  813.   BOOL      doesStripCache;
  814.   BOOL      doesStripCRLFCache;
  815.  
  816.  /*
  817.   * If there's no input queue, we've got trouble!
  818.   */
  819.   if (!inputQueue) return NULL;
  820.  
  821.   doesStripCache = [self doesStrip];
  822.   doesStripCRLFCache = [self doesStripCRLF];
  823.  
  824.  /*
  825.   * Go to the end of a delimited sequence.
  826.   */
  827.   for (rest = 0; rest < queueLength && inputQueue[rest] != aChar; rest++)
  828.     ;
  829.  
  830.  /*
  831.   * There isn't a delimited sequence.  We'll return an empty string.
  832.   * This condition also occurs if the queue is empty.
  833.   */
  834.   if (rest >= queueLength) {
  835.  
  836.     nextInput = (char *)NXZoneMalloc(aZone, 1);
  837.     if (!nextInput) {
  838.       SktReportUserMemError(self, STR_MallocFatalError);
  839.       return NULL;
  840.     }
  841.     nextInput[0] = '\0';
  842.     return nextInput;
  843.   }
  844.   else {
  845.  
  846.    /*
  847.     * We've got a delimited sequence.  Go just past aChar, and copy
  848.     * the sequence for return.  Mark the string end with '\0'.
  849.     */
  850.     rest++;
  851.  
  852.     nextInput = (char *)NXZoneMalloc(aZone, rest+1);
  853.     if (!nextInput) {
  854.       SktReportUserMemError(self, STR_MallocFatalError);
  855.       return NULL;
  856.     }
  857.     memcpy(nextInput, inputQueue, rest);
  858.     nextInput[rest] = '\0';
  859.   }
  860.  
  861.  /*
  862.   * Do goopy stripping stuff.  Check CRLF stripping, only doing so
  863.   * if the delimiter itself is stripped or the string ends in either
  864.   * a CR or an LF.
  865.   */
  866.   if (YES == doesStripCRLFCache) {
  867.       long int tail;
  868.  
  869.       tail = rest-1;
  870.       if (!ISENDLINE(nextInput[tail]) && YES == doesStripCache)
  871.         nextInput[tail--] = '\0';
  872.       for ( ; 0 <= tail && ISENDLINE(nextInput[tail]); tail--)
  873.         nextInput[tail] = '\0';
  874.   }
  875.   else if (YES == doesStripCache) {
  876.     nextInput[rest-1] = '\0';
  877.   }
  878.  
  879.  /*
  880.   * Pull back the queue.
  881.   */
  882.   memmove(inputQueue, inputQueue+rest, queueLength-rest);
  883.   queueLength -= rest;
  884.  
  885.  /*
  886.   * If the queue has shrunk to a significantly smaller size than
  887.   * the queue's allocated block, shrink the block to the minimum size.
  888.   * (We don't want the queue growing without bound! (Not that it will.)
  889.   */
  890.   if (queueLength < INQSIZE/2 && malloc_size(inputQueue) > INQSIZE) {
  891.     inputQueue = (char *)NXZoneRealloc(zone, inputQueue, INQSIZE);
  892.     if (!inputQueue) {
  893.       SktReportUserMemError(self, STR_ReallocFatalError);
  894.       queueLength = 0;
  895.     }
  896.   }
  897.  
  898.   return nextInput;
  899.  
  900. } /*inputToChar:inZone:*/
  901.  
  902. /***************************************************************************
  903. *                                                                          *
  904. * -nextInputLine                                                           *
  905. *                                                                          *
  906. * This method is a cover for -inputToChar:inZone:, with the user's         *
  907. * delimiter as the ...ToChar: argument, and NXDefaultMallocZone() as the   *
  908. * zone.                                                                    *
  909. *                                                                          *
  910. * ERROR CONDITION: If the allocation fails, a message is logged, and NULL  *
  911. * is returned.  Since this is indistinguishable from a situation in which  *
  912. * there is nothing in the queue, you can't currently catch a malloc        *
  913. * failure in this method.                                                  *
  914. *                                                                          *
  915. ***************************************************************************/
  916. - (char *)nextInputLine
  917. {
  918.   return (*inputToCharInZone)(self, @selector(inputToChar:inZone:),
  919.                              [self delimiter], NXDefaultMallocZone());
  920. }
  921.  
  922. /***************************************************************************
  923. *                                                                          *
  924. * -nextInputLineInZone:                                                    *
  925. *                                                                          *
  926. * This method is a cover for -inputToChar:inZone:, with the user's         *
  927. * delimiter as the ...ToChar: argument                                     *
  928. *                                                                          *
  929. * ERROR CONDITION: If the allocation fails, a message is logged, and NULL  *
  930. * is returned.  Since this is indistinguishable from a situation in which  *
  931. * there is nothing in the queue, you can't currently catch a malloc        *
  932. * failure in this method.                                                  *
  933. *                                                                          *
  934. ***************************************************************************/
  935. - (char *)nextInputLineInZone:(NXZone *)aZone
  936. {
  937.   return (*inputToCharInZone)(self, @selector(inputToChar:inZone:),
  938.                              [self delimiter], aZone);
  939. }
  940.  
  941. @end /*implementation SktSocketUser*/
  942.  
  943. /***************************************************************************
  944. ***************************************************************************/
  945.